﻿/******************************************************************************
 * Project: Processor Affinity Manager                                        *
 * File:    ManagedRegistryKey.cs                                             *
 * Author:  Aaron Jones                                                       *
 * Version: 1                                                                 *
 *                                                                            *
 * Description:                                                               *
 *    This file contains the definition and implementation of                 *
 *    ManagedRegistryEntry.  The class is a wrapper around accessing and      *
 *    modifying Windows registry entries.  The access to the registry is      *
 *    limited in that the keys and entries that can be edited are defined     *
 *    at construction and cannot be changed during the lifetime of the        *
 *    object.                                                                 *
 ******************************************************************************/

using System;
using Microsoft.Win32;
using System.Collections.Generic;

namespace ProcessorAffinityManager
{
    /// <summary>
    /// A simple wrapper around Microsoft.Win32.RegistryKey that allows
    /// limited access to the regsitry.
    /// </summary>
    public class ManagedRegistryKey
    {
        /// <summary>
        /// KeyValue represents a value in the Windows Registry.  It includes
        /// value and type information.
        /// </summary>
        public class KeyValue
        {
            public readonly object Value;
            public readonly RegistryValueKind ValueKind;

            public KeyValue(object value, RegistryValueKind valueKind)
            {
                Value = value;
                ValueKind = valueKind;
            }
        }

        #region Properties
        private readonly RegistryKey Root;
        private readonly string Path;
        private readonly List<string> ValidNames;
        #endregion

        #region Constructors
        /// <summary>
        /// Allow access only to the Default entry of a registry key.
        /// </summary>
        /// <param name="root">
        /// The root registry key.  Possible values are in Microsoft.Win32.Registry.
        /// </param>
        /// <param name="path">
        /// The slash-delimited path to the key that this ManagedRegistryKey will manage.
        /// </param>
        public ManagedRegistryKey(RegistryKey root, string path)
        {
            Root = root;
            Path = path;
            ValidNames = new List<string>();
            ValidNames.Add(null);
        }

        /// <summary>
        /// Allow access to a list of entries in the registry key.
        /// </summary>
        /// <param name="root">
        /// The root registry key.  Possible values are in Microsoft.Win32.Registry.
        /// </param>
        /// <param name="path">
        /// The slash-delimited path to the key that this ManagedRegistryKey will manage.
        /// </param>
        /// <param name="validNames">
        /// A list of entries that this ManagedRegistryKey can access and modify.
        /// </param>
        public ManagedRegistryKey(RegistryKey root, string path, string[] validNames)
        {
            Root = root;
            Path = path;
            ValidNames = new List<string>();
            foreach (string s in validNames)
                ValidNames.Add(s);
        }
        #endregion

        #region Key Accessors
        /// <summary>
        /// Access the Default entry of the registry key.
        /// </summary>
        /// <returns>
        /// A KeyValue representing the value in the Default entry of the managed
        /// registry key.
        /// </returns>
        public KeyValue GetValue()
        {
            return GetValue(null);
        }

        /// <summary>
        /// Access a specified entry of the registry key.
        /// </summary>
        /// <param name="name">The name of the entry to access.</param>
        /// <returns>
        /// A KeyValue representing the value in the specified entry of the managed
        /// registry key.
        /// </returns>
        public KeyValue GetValue(string name)
        {
            ValidateEntryName(name);
            ValidateEntryExists(name);
            RegistryKey key = Root.OpenSubKey(Path);
            return new KeyValue(key.GetValue(name), key.GetValueKind(name));
        }

        /// <summary>
        /// Access the Default entry of the registry key, and validate that its
        /// type is expected.
        /// </summary>
        /// <param name="valueKind">
        /// The expected type of data of the Default entry of the registry key.
        /// </param>
        /// <returns>
        /// An object representing the value of the Default entry of the managed
        /// registry key.
        /// </returns>
        public object GetValue(RegistryValueKind valueKind)
        {
            return GetValue(null, valueKind);
        }

        /// <summary>
        /// Access a specified entry of the registry key, and validate that its
        /// type is expected.
        /// </summary>
        /// <param name="name">The name of the entry to access.</param>
        /// <param name="valueKind">
        /// The expected type of data of the specified entry of the registry key.
        /// </param>
        /// <returns>
        /// An object representing the value of the specified entry of the managed
        /// registry key.
        /// </returns>
        public object GetValue(string name, RegistryValueKind valueKind)
        {
            ValidateEntryName(name);
            ValidateEntryExists(name);
            RegistryKey key = Root.OpenSubKey(Path);
            RegistryValueKind keyValueKind = key.GetValueKind(name);
            if (keyValueKind == valueKind)
                return key.GetValue(name);
            else
                throw new ArgumentException("Registry Key " + Path + ", name " + name + 
                    " was expected to be type " + valueKind.ToString() + " but was " +
                    keyValueKind.ToString() + " instead!");
        }
        #endregion

        #region Key Modifiers
        /// <summary>
        /// Modify the specified registy entry.
        /// </summary>
        /// <param name="name">The name of the entry to modify.</param>
        /// <param name="value">An object representing the value to write to the registry.</param>
        /// <param name="valueKind">The type of data the value parameter represents.</param>
        public void SetValue(string name, object value, RegistryValueKind valueKind)
        {
            ValidateEntryName(name);
            Root.CreateSubKey(Path).SetValue(name, value, valueKind);
        }

        /// <summary>
        /// Modify the specified registry entry.
        /// </summary>
        /// <param name="name">The name of the entry to modify.</param>
        /// <param name="value">An object representing the value to write to the registry.</param>
        public void SetValue(string name, object value)
        {
            ValidateEntryName(name);
            Root.CreateSubKey(Path).SetValue(name, value);
        }

        /// <summary>
        /// Modify the Default entry of the managed registry key.
        /// </summary>
        /// <param name="value">An object representing the value to write to the registry.</param>
        public void SetValue(object value)
        {
            ValidateEntryName(null);
            Root.CreateSubKey(Path).SetValue(null, value);
        }

        /// <summary>
        /// Delete the manged registry key (and any entries it may have).
        /// </summary>
        public void Delete()
        {
            Root.DeleteSubKey(Path);
        }

        /// <summary>
        /// Delete the specified entry of the managed registry key.
        /// </summary>
        /// <param name="name">The entry to delete.</param>
        public void DeleteValue(string name)
        {
            ValidateEntryName(name);
            Root.OpenSubKey(Path).DeleteValue(name);
        }
        #endregion Modifiers

        #region Verification Functions
        /// <summary>
        /// Determine if the managed registry key exists.
        /// </summary>
        /// <returns>True if the managed registry key exists, false otherwise.</returns>
        public bool Exists()
        {
            if (Root.OpenSubKey(Path) == null)
                return false;
            else
                return true;
        }

        /// <summary>
        /// Determine a specified entry exists in the managed registy key.
        /// </summary>
        /// <param name="name">The name of the entry to query.</param>
        /// <returns>True if the entry exists, false otherwise.</returns>
        public bool EntryExists(string name)
        {
            ValidateEntryName(name);

            if (Exists())
            {
                // The default key will always exist.
                if (name == null)
                    return true;

                foreach (string s in Root.OpenSubKey(Path).GetValueNames())
                    if (s == name)
                        return true;
            }

            return false;
        }

        /// <summary>
        /// Throws if the managed registry key cannot acces the specified entry.
        /// </summary>
        /// <param name="name">The name of the entry to query.</param>
        private void ValidateEntryName(string name)
        {
            if (!ValidNames.Contains(name))
                throw new ArgumentException("Registry Key " + Path + ", name " + name + " is not valid!");
        }

        /// <summary>
        /// Throws if the entry does not exist.
        /// </summary>
        /// <param name="name">The name of the entry to query.</param>
        private void ValidateEntryExists(string name)
        {
            if(!EntryExists(name))
                throw new ArgumentException("Registry Key " + Path + ", name " + name + " does not exist!");
        }
        #endregion
    }
}
